/**
 * Copyright 2000 self.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 *
 * @author Ashin Wimalajeewa
 */
package self.amigo.tool;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenuItem;

import self.amigo.diag.ContextDiagram;
import self.amigo.diag.ScreenSketch;
import self.amigo.diag.bpm.ProcessRoadmap;
import self.amigo.diag.uml.ActivityDiagram;
import self.amigo.diag.uml.ClassDiagram;
import self.amigo.diag.uml.CollaborationDiagram;
import self.amigo.diag.uml.ComponentDiagram;
import self.amigo.diag.uml.DeploymentDiagram;
import self.amigo.diag.uml.PackageDiagram;
import self.amigo.diag.uml.SequenceDiagram;
import self.amigo.diag.uml.StateTransitionDiagram;
import self.amigo.diag.uml.UseCaseDiagram;
import self.amigo.io.DiagramReader;
import self.amigo.io.DiagramWriter;
import self.amigo.module.CloseInfo;
import self.amigo.module.IEditingHost;
import self.amigo.module.OpenInfo;
import self.amigo.module.SaveInfo;
import self.gee.IDiagram;
import self.gee.IDiagramLayer;
import self.gee.editor.DiagramCommandUtils;
import self.io.PathUtils;

public class DiagramManager {
  static private int untitledCount = 0;

  private IEditingHost        host;
  private AgileModelingShell  shell;
  public OpenDiagramInfo      active;
  private List                openDiagrams = new ArrayList();

  private JMenuItem           fileSaveItem;
  private JMenuItem           fileSaveAsItem;
  private JMenuItem           fileSaveToCbItem;

  public DiagramManager( IEditingHost h, AgileModelingShell s ) {
    host = h;
    shell = s;
    initMenus();
  }

  private void initMenus() {
    fileSaveItem = (JMenuItem) host.getValue( IEditingHost.FILE_SAVE_MI_KEY );
    fileSaveAsItem = (JMenuItem) host.getValue( IEditingHost.FILE_SAVE_AS_MI_KEY );
    fileSaveToCbItem = (JMenuItem) host.getValue( IEditingHost.FILE_SAVE_TO_CB_MI_KEY );
  }

  public void activateMenus( boolean activate ) {
    boolean enableGenSave = false;
    boolean enableSave = false;
    if ( activate ) {
      enableGenSave = true;
      OpenInfo info = active.info;
      enableSave = ( info.type == OpenInfo.FILE_SYSTEM );
    }
    fileSaveItem.setEnabled( enableSave );
    fileSaveAsItem.setEnabled( enableGenSave );
    fileSaveToCbItem.setEnabled( enableGenSave );
  }

  public void     open( OpenInfo open ) throws IOException {
    Reader rdr = open.getReader();
    IDiagram d = null;
    try {
      try {
        DiagramReader dr = new DiagramReader( rdr );
        d = dr.readDiagram();
      } finally {
        rdr.close();
      }
    } catch ( IOException err ) {
      if ( d != null )
        d.dispose();
      throw err;
    }
    OpenDiagramInfo info = new OpenDiagramInfo( d, open );
    openDiagrams.add( info );
    if ( open.type == OpenInfo.CLIPBOARD )
      d.setDirty();
    loadDiagram( info );
  }

  public void     getCloseInfos( java.util.List store, boolean activeOnly ) {
    if ( activeOnly )
      store.add( createCloseInfo(active) );
    else {
      int max = openDiagrams.size();
      for ( int cntr = 0; cntr < max; cntr++ )
        store.add( createCloseInfo((OpenDiagramInfo) openDiagrams.get(cntr)) );
    }
  }

  public void     save( OpenInfo open, SaveInfo save ) throws IOException {
    OpenDiagramInfo target = getOpenDiagramInfo( open );
    target.save( save );
  }

  public void     close( OpenInfo info ) {
    OpenDiagramInfo target = getOpenDiagramInfo( info );
    target.dispose();
    if ( openDiagrams.size() > 0 )
      loadDiagram( (OpenDiagramInfo) openDiagrams.get(0) );
    else {
      loadDiagram( null );
      host.unmount();
    }
  }

  public void     getWindowOpenCommandActions( java.util.List store ) {
    int max = openDiagrams.size();
    for ( int cntr = 0; cntr < max; cntr++ )
      store.add( new WindowOpenAction((OpenDiagramInfo) openDiagrams.get(cntr)) );
  }

  public void     getNewCommandActions( java.util.List store ) {
    store.add( new FileNewAction("Process Roadmap", ProcessRoadmap.class) );
    store.add( new FileNewAction("Screen Sketch", ScreenSketch.class) );
    store.add( new FileNewAction("Context Diagram", ContextDiagram.class) );
    store.add( null );
    store.add( new FileNewAction("Package Diagram", PackageDiagram.class) );
    store.add( new FileNewAction("Activity Diagram", ActivityDiagram.class) );
    store.add( new FileNewAction("Use-Case Diagram", UseCaseDiagram.class) );
    store.add( new FileNewAction("Class Diagram", ClassDiagram.class) );
    store.add( new FileNewAction("State Transition Diagram", StateTransitionDiagram.class) );
    store.add( new FileNewAction("Sequence Diagram", SequenceDiagram.class) );
    store.add( new FileNewAction("Collaboration Diagram", CollaborationDiagram.class) );
    store.add( new FileNewAction("Component Diagram", ComponentDiagram.class) );
    store.add( new FileNewAction("Deployment Diagram", DeploymentDiagram.class) );
  }

  private OpenDiagramInfo getOpenDiagramInfo( OpenInfo info ) {
    OpenDiagramInfo ret = null;
    int max = openDiagrams.size();
    for ( int cntr = 0; cntr < max; cntr++ ) {
      ret = (OpenDiagramInfo) openDiagrams.get( cntr );
      if ( ret.info == info )
        return ret;
    }
    return null;
  }

  private CloseInfo createCloseInfo( OpenDiagramInfo diag ) {
    OpenInfo open = diag.info;
    IDiagram d = diag.diagram;
    return new CloseInfo( open, d.getTitle(), d.isModified() );
  }

  private void loadDiagram( OpenDiagramInfo info ) {
    if ( info == active )
      return;
    shell.init();
    if ( active != null )
      shell.unmountDiagram();
    active = info;
    boolean activeDiagram = active != null;
    if ( activeDiagram )
      shell.mountDiagram();
    shell.resetMenuItems( activeDiagram );
  }

  private class WindowOpenAction extends AbstractAction {
  	
	/**
	 * If the internal state of this class ever changes in such a way that it can't be defaulted,
	 * then the {@link #serialVersionUID} should be incremented to ensure serialized instances cleanly fail.  
	 */
	private static final long serialVersionUID = 1;
	
    private OpenDiagramInfo info;

    public WindowOpenAction( OpenDiagramInfo info ) {
      super( info.diagram.getTitle() );
      this.info = info;
    }

    public void actionPerformed( ActionEvent e ) {
      loadDiagram( info );
//      String nm = info.diagram.getClass().getName();
//      nm += "::" + info.diagram.getTitle();
//      host.setTitle( nm );
    }
  }

  private class FileNewAction extends AbstractAction {

	/**
	 * If the internal state of this class ever changes in such a way that it can't be defaulted,
	 * then the {@link #serialVersionUID} should be incremented to ensure serialized instances cleanly fail.  
	 */
	private static final long serialVersionUID = 1;
	
    private Class diagramClass;

    public FileNewAction( String commandName, Class diagClass ) {
      super( commandName );
      diagramClass = diagClass;
    }

    public void actionPerformed( ActionEvent e ) {
      try {
        IDiagram d = (IDiagram) diagramClass.newInstance();
        d.setTitle( "untitled " + untitledCount++ );
        OpenInfo open = new OpenInfo( OpenInfo.NEW, null );
        OpenDiagramInfo info = new OpenDiagramInfo( d, open );
        openDiagrams.add( info );
        loadDiagram( info );
      } catch ( Exception err ) {
        err.printStackTrace();
      }
    }
  }

  public class OpenDiagramInfo {
    public IDiagram diagram;
    public OpenInfo info;

    public OpenDiagramInfo( IDiagram d, OpenInfo i ) {
      set( d, i );
    }

    public void set( IDiagram d, OpenInfo i ) {
      diagram = d;
      info = i;
    }

    public void set( IDiagram d ) {
      diagram = d;
    }

    public void set( OpenInfo i ) {
      info = i;
    }

    public void save( SaveInfo save ) throws IOException {
      if (save.target == null || save.target.getName().endsWith(".amigoml")) 
    	  saveAsAmigoXml(save);
      else 
    	  saveAsImageType(save);
    }

	private void saveAsAmigoXml(SaveInfo save) throws IOException {
		Writer wrt = save.getWriter( info );
		  try {
		    DiagramWriter dw = new DiagramWriter( wrt );
		    dw.writeDiagram( diagram );
		  } finally {
		    wrt.close();
		  }
		  OpenInfo newInfo = save.getOpenInfoOnceSaved( info );
		  if ( newInfo != info ) {
		    info = newInfo;
		    shell.resetMenuItems( active != null );
		  }
	}
    
    public void saveAsImageType(SaveInfo saveInfo) throws IOException {
    	Canvas canvas = new Canvas();
    	canvas.setBounds(0, 0, diagram.getWidth(), diagram.getHeight());
    	JFrame tmpFrame = new JFrame();
    	tmpFrame.setSize(300, 300);
    	tmpFrame.getContentPane().setLayout(new BorderLayout());
    	tmpFrame.add(canvas, BorderLayout.CENTER);
    	tmpFrame.setVisible(true);
    	try {
        	Image img = canvas.createImage(diagram.getWidth(), diagram.getHeight());
        	Graphics g = img.getGraphics();
    		IDiagramLayer layer = diagram.getContext();
        	layer.paint(g);
        	String ext = PathUtils.toFileExtention(saveInfo.target.getName());
        	ImageIO.write((RenderedImage)img, ext, saveInfo.target);
    	} finally {
        	tmpFrame.setVisible(false);
    	}
    }

    public void dispose() {
      diagram.dispose();
      DiagramCommandUtils.release( diagram );
      openDiagrams.remove( this );
    }
  }
}